package asdf.springmvc;

import com.google.common.base.Joiner;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;

@SuppressWarnings({"rawtypes", "unchecked"})
public class RequestWrapper extends HttpServletRequestWrapper {

    private static final Logger LGR = LoggerFactory.getLogger(RequestWrapper.class);

    private static final ThreadLocal<Map<String, String[]>> DECRYPTED_PARAMS_HOLDER = new ThreadLocal<>();

    private RequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getParameter(String name) {
        if (DECRYPTED_PARAMS_HOLDER.get().containsKey(name)) {
            String[] values = DECRYPTED_PARAMS_HOLDER.get().get(name);
            if (values.length == 1) {
                return values[0];
            } else {
                return Joiner.on(',').join(values);
            }
        } else {
            return super.getParameter(name);
        }
    }

    @Override
    public Map getParameterMap() {
        Map originMap = super.getParameterMap();
        Map result = new HashMap<>(originMap);
        result.putAll(DECRYPTED_PARAMS_HOLDER.get());
        return result;
    }

    @Override
    public Enumeration getParameterNames() {
        return Collections.enumeration(this.getParameterMap().entrySet());
    }

    @Override
    public String[] getParameterValues(String name) {
        if (DECRYPTED_PARAMS_HOLDER.get().containsKey(name)) {
            return DECRYPTED_PARAMS_HOLDER.get().get(name);
        } else {
            return super.getParameterValues(name);
        }
    }

    static RequestWrapper build(HttpServletRequest request) {
        RequestWrapper yrw = new RequestWrapper(request);
        String decryptTarget = request.getParameter("param");
        if (StringUtils.isBlank(decryptTarget)) {
            return yrw;
        }
        String v = request.getParameter("v");
        LGR.debug("wrap request[{}]: parameters={}, v={}", request.hashCode(), decryptTarget, v);

        try {
            decryptTarget = decryptTarget.replace(" ", "+");
            Map<String, String[]> params = new HashMap<>();
            DECRYPTED_PARAMS_HOLDER.set(params);
            String decrypted = null;
            try {
                decrypted = CryptUtils.decryptText(decryptTarget);
            } catch (Exception e) {
                LGR.error("parameters[" + decryptTarget + "] of request[" + request.hashCode()
                        + "] decrypt failed. v[" + v + "]", e);
                return null;
            }
            LGR.debug("decrypted params of request[{}]:{}", request.hashCode(), decrypted);
            Map<String, ArrayList<String>> multiValueParams = new HashMap<>();
            if (request.getMethod().equals("GET")) {
                String[] nameAndValueArr = decrypted.split("&");
                for (String nameAndValue : nameAndValueArr) {
                    String[] arr = nameAndValue.split("=");
                    if (arr.length != 2) {
                        continue;
                    }
                    String key = arr[0], value = arr[1];
                    if (params.containsKey(key)) {
                        if (multiValueParams.containsKey(key)) {
                            multiValueParams.get(key).add(value);
                        } else {
                            ArrayList<String> values = new ArrayList<>();
                            multiValueParams.put(key, values);
                            values.add(params.get(key)[0]);
                            values.add(value);
                        }
                    } else {
                        params.put(key, new String[]{ value });
                    }
                    if (multiValueParams.size() > 0) {
                        for (Map.Entry<String, ArrayList<String>> me : multiValueParams.entrySet()) {
                            params.put(me.getKey(), me.getValue().toArray(new String[]{}));
                        }
                    }
                }
            } else if (request.getMethod().equals("POST")) {
                Map<String, String> map = new Gson().fromJson(decrypted, new TypeToken<Map<String, List<Object>>>() {}.getType());
                if (map != null && map.size() > 0) {
                    for (Map.Entry<String, String> e : map.entrySet()) {
                        params.put(e.getKey(), new String[]{e.getValue()});
                    }
                }
            } else {
                LGR.error("unsupportted method of request:" + request.getMethod());
            }
        } catch (Exception e) {
            LGR.error("create instance of YungeRequestWrapper failed.", e);
            return null;
        }
        return yrw;
    }

}
